package com.pink.filter;

import com.pink.entity.config.Constants;
import com.pink.entity.vo.RestBeanVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Component
@Order(Constants.ORDER_LIMIT)
@Slf4j
public class FlowLimitFilter extends HttpFilter {

    @Resource
    StringRedisTemplate template;

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String address = request.getRemoteAddr();
        log.info("请求ip:[{}]",address);
        if(this.tryCount(address)){
            chain.doFilter(request, response);
        }else{
            this.writeBlockMessage(response);
        }

    }
    private void writeBlockMessage(HttpServletResponse response) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.getWriter().write(RestBeanVo.failure("操作异常，请稍后再试").asJsonString());
    }
    private boolean tryCount(String ip){
        synchronized (ip.intern()){
            if(template.hasKey(Constants.FLOW_LIMIT_BLOCK + ip)) return false;
            return this.limitPeriodCheck(ip);
        }
    }
    private boolean limitPeriodCheck(String ip){
        if(template.hasKey(Constants.FLOW_LIMIT_COUNTER + ip)){
            long increment = Optional.ofNullable(template.opsForValue().increment(Constants.FLOW_LIMIT_COUNTER + ip)).orElse(0L);
            if(increment > 200){
                template.opsForValue().set(Constants.FLOW_LIMIT_BLOCK+ip,"",30,TimeUnit.SECONDS);//30秒
                log.warn("[{}]被封ip",ip);
                return false;
            }
        }else{
            template.opsForValue().set(Constants.FLOW_LIMIT_COUNTER + ip,"1",3, TimeUnit.MINUTES);
        }
        return true;
    }
}
